import matplotlib
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D   # 只需导入即可激活 3D 功能

# -------------------------------------------------
# 1️⃣  数据
# -------------------------------------------------
P0 = np.array([1.0, 0.0])
P1 = np.array([2.0, -1.0])
P2 = np.array([3.0, -1.0])
P3 = np.array([4.0, 2.0])
control_pts = [P0, P1, P2, P3]          # (x, y)
weights      = [2, 1, 1, 2]           # 对应的权重
t_val        = 0.6                    # 需要演示的参数

# -------------------------------------------------
# 2️⃣  有理 de Casteljau（返回每一层的齐次点，便于绘图）
# -------------------------------------------------
def rational_de_casteljau_steps(t, ctrl_pts, wts):
    """
    返回每一层的齐次点列表（包括原始层），
    便于在三维空间中绘制插值连线。
    """
    pts = np.array(ctrl_pts, dtype=float)          # (n+1, 2)
    w   = np.array(wts, dtype=float).reshape(-1, 1)  # (n+1, 1)

    # 齐次向量 (wx, wy, w)
    homo = np.hstack([pts * w, w])                 # (n+1, 3)

    layers = [homo.copy()]                        # 第 0 层（控制点）

    n = homo.shape[0] - 1
    for r in range(1, n + 1):
        print(r, homo)
        homo[:n - r + 1] = (1 - t) * homo[:n - r + 1] + t * homo[1:n - r + 2]
        layers.append(homo[:n - r + 1].copy())     # 保存本层的点

    return layers   # layers[0] … layers[-1]（最后一层只有 1 个点）

# -------------------------------------------------
# 3️⃣  计算所有层的点
# -------------------------------------------------
layers_homo = rational_de_casteljau_steps(t_val, control_pts, weights)

# -------------------------------------------------
# 4️⃣  绘图
# -------------------------------------------------
matplotlib.rcParams['font.family'] = 'Microsoft YaHei'  # Windows 常用

plt.figure(figsize=(10, 8))
ax3d = plt.axes(projection='3d')
ax3d.set_xlabel('x')
ax3d.set_ylabel('y')
ax3d.set_zlabel('w (权重)')
ax3d.set_title(f'有理 Bézier de Casteljau 过程 (t = {t_val})')

# 颜色循环，用来区分不同层
colors = ['red', 'green', 'blue', 'orange']

# ① 绘制每一层的连线（齐次空间）
for i, layer in enumerate(layers_homo):
    xs, ys, ws = layer[:, 0], layer[:, 1], layer[:, 2]
    ax3d.plot(xs, ys, ws, '-o', color=colors[i % len(colors)],
              label=f'第 {i} 层 ({len(layer)} 点)')

# ② 在二维平面上绘制完整的有理 Bézier 曲线（投影后）
def rational_bezier_point(t, ctrl_pts, wts):
    """单点求值（投影后）"""
    pts = np.array(ctrl_pts, dtype=float)
    w   = np.array(wts, dtype=float).reshape(-1, 1)
    homo = np.hstack([pts * w, w])
    n = homo.shape[0] - 1
    for r in range(1, n + 1):
        homo[:n - r + 1] = (1 - t) * homo[:n - r + 1] + t * homo[1:n - r + 2]
    X, Y, W = homo[0]
    return np.array([X / W, Y / W])

ts = np.linspace(0, 1, 200)
curve = np.array([rational_bezier_point(tt, control_pts, weights) for tt in ts])
ax3d.plot(curve[:, 0], curve[:, 1], np.zeros_like(curve[:, 0]),  # z=0 为投影平面
          color='steelblue', linewidth=2, label='投影后 Bézier 曲线')

# ③ 标记最终点（第 n 层唯一的点）
final_pt_homo = layers_homo[-1][0]          # 齐次坐标
final_pt_xy   = final_pt_homo[:2] / final_pt_homo[2]
ax3d.scatter(*final_pt_homo, color='gold', s=120, edgecolor='black',
            label='t = {:.2f} 的曲线点 (齐次)'.format(t_val))


ax3d.legend(loc='upper left')
ax3d.view_init(elev=30, azim=-60)   # 调整视角，便于观察

plt.show()
